package utils

import (
	"bytes"
	"errors"
	"fmt"
	"log"
	"os"
	"path/filepath"
	"reflect"
	"strconv"
	"time"

	"github.com/rs/zerolog"
	"github.com/xuri/excelize/v2"
)

// CreateExcel 创建excel并且写入数据
//
// 参数
//
//	excelName: excel文件名
//	data: 需要写入的数据
//
// 返回
//
//	string: excel的绝对路径
//	error: 产生的错误信息
//
//goland:noinspection GoUnhandledErrorResult
func CreateExcel(excelName string, data [][]string) (string, error) {
	f := excelize.NewFile()

	defer f.Close()

	// 创建工作表，刚创建的excel默认是没有工作表的
	_, err := f.NewSheet("sheet1")

	if err != nil {
		log.Printf("设置工作表名称错误===%v", err)
		return "", err
	}
	// 继续创建工作表，这里macOS预览的时候才会默认激活了sheet1，不需要的可以删除该代码
	_, _ = f.NewSheet("sheet2")

	// 设置excel表头内容
	f.SetCellValue("Sheet1", "A1", "快递名称")
	f.SetCellValue("Sheet1", "B1", "快递单号")
	f.SetCellValue("Sheet1", "C1", "取件码")
	f.SetCellValue("Sheet1", "D1", "发件人姓名")
	f.SetCellValue("Sheet1", "E1", "发件人手机")
	f.SetCellValue("Sheet1", "F1", "发件人地址")
	f.SetCellValue("Sheet1", "G1", "发件人身份证号")
	f.SetCellValue("Sheet1", "H1", "创建人")
	f.SetCellValue("Sheet1", "I1", "创建时间")
	f.SetCellValue("Sheet1", "J1", "修改人")
	f.SetCellValue("Sheet1", "K1", "修改时间")

	if len(data) > 0 {
		// 从第二行开始写入数据
		for r, row := range data {
			// 行号从2开始
			for c, value := range row {
				colName, err := excelize.ColumnNumberToName(c + 1)
				if err != nil {
					return "", err
				}
				// 行索引从2开始，所以r+2（r从0开始，第一行是标题，所以第二行从索引2开始）
				cell := fmt.Sprintf("%s%d", colName, r+2)
				f.SetCellValue("Sheet1", cell, value)
			}
		}
	}

	// 设置活动的工作表
	f.SetActiveSheet(0)

	currentPath, err := os.Getwd()
	if err != nil {
		log.Printf("获取当前目录错误===%v", err)
		return "", err
	}

	log.Println("获取当前目录:", currentPath)

	// 确保目录存在
	excelDir := currentPath + "/excel"
	if _, err := os.Stat(excelDir); os.IsNotExist(err) {
		// 创建目录
		if err := os.MkdirAll(excelDir, os.ModePerm); err != nil {
			log.Printf("创建目录===%s错误：%+v", excelDir, err)
			return "", err
		}
	}

	// 按指定路径保存文件
	filePath := excelDir + "/" + excelName
	if err := f.SaveAs(filePath); err != nil {
		log.Printf("保存excel到指定路径===%s错误：%+v", filePath, err)
		return "", err
	}

	absPath, err := filepath.Abs(excelName)
	if err != nil {
		log.Printf("获取excel名称%s绝对路径错误：%+v", excelName, err)
		return "", err
	}

	return absPath, nil
}

// ReadExcelFile 读取Excel文件中的数据
//
// 参数
//
//	excelName: excel文件名
//	data: 需要写入的数据
//
// 返回
//
//	string: excel的绝对路径
//	error: 产生的错误信息
//
//goland:noinspection GoUnhandledErrorResult
func ReadExcelFile(filePath string) ([][]string, error) {
	var allRows [][]string
	logger := zerolog.DefaultContextLogger

	f, err := excelize.OpenFile(filePath)
	if err != nil {
		logger.Error().Err(err).Msg("打开excel错误")
		return nil, errors.New("读取excel文件失败")
	}

	defer f.Close()

	// 获取所有工作表
	sheetList := f.GetSheetList()
	if len(sheetList) == 0 {
		logger.Warn().Msg("excel文件中没有工作表")
		return nil, errors.New("excel文件中没有工作表")
	}

	// 循环工作表
	for _, sheetName := range sheetList {
		// 读取所有行
		rows, err := f.GetRows(sheetName)
		if err != nil {
			logger.Error().Err(err).Msg("读取Excel所有行错误")
			return nil, errors.New("读取Excel文件数据失败")
		}

		if len(rows) == 0 {
			continue
		}

		// 跳过当前工作表的标题行（第一行）
		if len(rows) > 1 {
			allRows = append(allRows, rows[1:]...)
		}
	}

	return allRows, nil
}

// WriteDataToExcelBufferProcess 无中间文件生成excel(递归处理嵌套结构体)
func WriteDataToExcelBufferProcess[T any](headers []string, list []T, sheetName string, headerBgColor bool) (*bytes.Buffer, error) {
	if len(list) <= 0 {
		return nil, fmt.Errorf("数据列表为空，无法导出")
	}

	// 创建列宽记录器 (初始化值为表头宽度)
	f, maxColWidths, err := setHeaderContentAndWidth(sheetName, headers)
	if err != nil {
		zerolog.DefaultContextLogger.Error().Err(err).Str("sheetName", sheetName).Msg("设置标题内存与记录宽度错误")
		return nil, err
	}

	if headerBgColor {
		if err = setHeaderBgColor(f, sheetName, headers); err != nil {
			zerolog.DefaultContextLogger.Error().Err(err).Str("sheetName", sheetName).Msg("excel设置标题背景色错误")
			return nil, err
		}
	}

	// 递归处理嵌套结构体的函数
	var processValue func(reflect.Value, int, int) (int, error)
	processValue = func(v reflect.Value, rowIndex, startColIndex int) (int, error) {
		colIndex := startColIndex

		// 处理指针类型
		if v.Kind() == reflect.Ptr {
			if v.IsNil() {
				return colIndex, nil
			}
			v = v.Elem()
		}

		// 处理结构体类型
		if v.Kind() == reflect.Struct {
			// 检查是否为时间类型
			if _, isTime := v.Interface().(time.Time); isTime {
				// 时间类型走正常处理流程
			} else {
				// 递归处理结构体字段
				for i := 0; i < v.NumField(); i++ {
					field := v.Field(i)
					fieldType := v.Type().Field(i)

					// 跳过不可导出的字段
					if !field.CanInterface() {
						continue
					}

					// 跳过标记为忽略的字段
					if tag := fieldType.Tag.Get("gorm"); tag == "-" {
						continue
					}

					// 递归处理字段
					newColIndex, err := processValue(field, rowIndex, colIndex)
					if err != nil {
						return colIndex, err
					}
					colIndex = newColIndex
				}
				return colIndex, nil
			}
		}

		// 确保不超过表头列数
		if colIndex >= len(headers) {
			return colIndex, nil
		}

		colName := indexToColumn(colIndex)
		cell := colName + strconv.Itoa(rowIndex+2)

		// 格式化值，并且处理time.Time为零与是否有时分秒
		var value string
		switch val := v.Interface().(type) {
		case time.Time:
			if val.IsZero() {
				value = ""
			} else {
				if val.Hour() == 0 && val.Minute() == 0 && val.Second() == 0 {
					value = val.Format(time.DateOnly)
				} else {
					value = val.Format(time.DateTime)
				}
			}
		default:
			value = fmt.Sprintf("%v", val)
		}

		if err = f.SetCellValue(sheetName, cell, value); err != nil {
			zerolog.DefaultContextLogger.Error().Err(err).Str("cell", cell).Msg("设置表格单元格的值错误")
			return colIndex, err
		}

		// 更新最大列宽
		maxColWidths[colName] = max(maxColWidths[colName], visualWidth(value))

		return colIndex + 1, nil
	}

	// 写入数据
	for rowIndex, record := range list {
		v := reflect.ValueOf(record)

		// 递归处理每条记录
		_, err = processValue(v, rowIndex, 0)
		if err != nil {
			return nil, err
		}
	}

	// 设置自适应列宽
	setColumnAdaptiveWidth(f, sheetName, maxColWidths)

	// 写入到内存缓冲区
	var buf = bytes.NewBuffer(make([]byte, 0, 1<<20))
	if err = f.Write(buf); err != nil {
		zerolog.DefaultContextLogger.Error().Err(err).Msg("excel写入缓冲区错误")
		return nil, err
	}
	return buf, nil
}

// indexToColumn 将索引转换为 Excel 列名
func indexToColumn(index int) string {
	result := ""
	for index >= 0 {
		result = string(rune('A'+index%26)) + result
		index = (index-index%26)/26 - 1
	}
	return result
}

func setHeaderBgColor(f *excelize.File, sheetName string, headers []string) error {
	if len(headers) <= 0 {
		return errors.New("excel标题切片不能为空")
	}
	// 创建标题样式 - 灰色背景 + 加粗 + 居中
	headerStyle, err := f.NewStyle(&excelize.Style{
		Fill: excelize.Fill{
			Type:    "pattern",
			Pattern: 1,
			Color:   []string{"#EBEBEB"}, // 黄色背景
		},
		Font: &excelize.Font{
			Bold: true, // 加粗
		},
		Alignment: &excelize.Alignment{
			Horizontal: "center", // 水平居中
			Vertical:   "center", // 垂直居中
		},
	})
	if err != nil {
		zerolog.DefaultContextLogger.Error().Err(err).Msg("创建标题样式错误")
		return fmt.Errorf("创建标题样式失败: %v", err)
	}

	for i := range headers {
		cell := indexToColumn(i) + "1"
		if err = f.SetCellStyle(sheetName, cell, cell, headerStyle); err != nil {
			zerolog.DefaultContextLogger.Error().Err(err).Str("cell", cell).Msg("设置标题单元格样式错误")
			return fmt.Errorf("设置标题单元格样式失败: %v", err)
		}
	}

	return nil
}

// setHeaderContentAndWidth 设置表格标题内容与记录宽度
func setHeaderContentAndWidth(sheetName string, headers []string) (*excelize.File, map[string]int, error) {
	// 确保工作表存在
	if sheetName == "" {
		sheetName = "Sheet1"
	}

	f := excelize.NewFile()
	defer func() {
		if err := f.Close(); err != nil {
			zerolog.DefaultContextLogger.Error().Err(err).Msg("关闭excel文件失败")
		}
	}()

	// 创建或激活指定名称的工作表
	defaultSheet := f.GetSheetName(0)

	// 检查目标工作表是否存在（兼容旧版本）
	targetSheetExists := false
	for i := 0; i < f.SheetCount; i++ {
		if f.GetSheetName(i) == sheetName {
			targetSheetExists = true
			break
		}
	}

	if !targetSheetExists {
		// 如果目标工作表不存在则创建
		sheetIndex, err := f.NewSheet(sheetName)
		if err != nil {
			return nil, nil, err
		}
		zerolog.DefaultContextLogger.Info().Msgf("excel指定的工作表不存在，创建工作表===%s,索引===%d", sheetName, sheetIndex)
		// 创建额外的工作表，macOS预览的时候就能看到激活的工作表名称了
		_, _ = f.NewSheet("sheet2")
		// 删除默认创建的工作表（如果存在）
		if defaultSheet != sheetName && f.SheetCount > 1 {
			if err = f.DeleteSheet(defaultSheet); err != nil {
				zerolog.DefaultContextLogger.Error().Err(err).Str("defaultSheet", defaultSheet).Msg("excel删除默认工作表错误")
				return nil, nil, err
			}
		}
		// 设置第一个工作表为激活状态
		f.SetActiveSheet(0)
	}

	maxColWidths := make(map[string]int)

	for i, header := range headers {
		colName := indexToColumn(i)
		cell := colName + "1"
		if err := f.SetCellValue(sheetName, cell, header); err != nil {
			zerolog.DefaultContextLogger.Error().Err(err).Str("colName", colName).Str("cell", cell).Msg("excel设置标题内容错误")
			return nil, nil, err
		}
		//f.SetColWidth(sheet, indexToColumn(i), indexToColumn(i), float64(len(header)*2))
		maxColWidths[colName] = max(maxColWidths[colName], visualWidth(header))
	}
	return f, maxColWidths, nil
}

// setColumnAdaptiveWidth 设置自适应列宽
func setColumnAdaptiveWidth(f *excelize.File, sheetName string, maxColWidths map[string]int) {
	if len(maxColWidths) <= 0 {
		return
	}
	for colName, width := range maxColWidths {
		_ = f.SetColWidth(sheetName, colName, colName, float64(width+5))
	}
}

// visualWidth 计算字符串的“视觉宽度”，中文算2，英文算1
func visualWidth(s string) int {
	width := 0
	for _, r := range s {
		if r > 127 {
			width += 2 // 中文等宽字符
		} else {
			width += 1 // 英文/数字
		}
	}
	return width
}
